Finite Impulse Response Filter


REMEZ

REMEZ is a library that allows designing: Low Pass, Band Pass, High Pass, Hilbert and Differentiator filters. The REMEZ library is based on the Parks-McClellan algorithm, see Discrete-Time Signal Processing (Alan V. Oppenheim and Ronal W. Schafer).
REMEZ es una librería que permite diseñar filtros: Pasa Baja, Pasa Banda, Pasa Alta, de Hilbert y Diferenciadores. La librería de REMEZ está basa en el algoritmo de Parks-McClellan, vea Discrete-Time Signal Processing (Alan V. Oppenheim and Ronal W. Schafer).

Problem 1
Create a project called RemezTest to create to design filters using the REMEZ algorithm. The program must display the Impulse and Frequency Response. Use a slider to control the length of the filter.
Cree un proyecto llamado RemezTest para filtros usando el algoritmo de REMEZ. El programa debe mostrar la respuesta al impulso y la respuesta en frecuencia. Use un slider para la longitud del filtro.

Solution A
Create a Wintempla Dialog Application. Using Wintempla create the GUI shown below. After inserting the slider for the length of the filter, double click the control and mark the HSCROLL event.
Cree una aplicación de Diálogo de Wintempla. Use Wintempla para la crear la GUI mostrada debajo. Después de insertar el slider para la longitud del filtro, haga doble clic en el control para marcar el evento de HSCROLL.

RemezTestGui

Solution B
Edit the RemezTest.h file as shown.
Edite el archivo RemezTest.h como se muestra.

RemezTest.h
#pragma once //______________________________________ RemezTest.h
#include "resource.h"
class RemezTest: public Win::Dialog
{
public:
     RemezTest()
     {
     }
     ~RemezTest()
     {
     }
     Math::Remez remez;
     const size_t numPoints = 2048;
     valarray<double> freqRad;
     void UpdateGraphs();
protected:
     . . .
};


Solution C
Edit the RemezTest.cpp file as shown.
Edite el archivo RemezTest.cpp como se muestra.

RemezTest.Cpp
. . .
void RemezTest::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldLength
     sldLength.SetRange(2, 100);
     sldLength.Position = 55;
     tbxLength.IntValue = 55;
     //________________________________________________________ xyFreqResp
     xyFreqResp.CaptionX = L"Frequency (radians)";
     xyFreqResp.CaptionY = L"|H[f]| dB";
     xyFreqResp.MinX= 0.0; // rads
     xyFreqResp.MaxX = 3.2; // rads
     xyFreqResp.MinY= -100.0; // dB
     xyFreqResp.MaxY= 10.0; // dB
     xyFreqResp.DivisionCountX = 16;
     xyFreqResp.DivisionCountY = 11;
     xyFreqResp.SubDivisionCountX = 0;
     xyFreqResp.ColorMode = WIN_COLOR_MODE_DARK;
     xyFreqResp.TextColor = RGB(180, 180, 190);
     xyFreqResp.Graphs.Add(numPoints);
     freqRad.resize(numPoints);
     const double deltaFreq = M_PI / (numPoints - 1);
     for (int i = 0; i < numPoints; i++)
     {
          freqRad[i] = i * deltaFreq;
          xyFreqResp.Graphs[0][i].x = i * deltaFreq;
          xyFreqResp.Graphs[0][i].y = -200.0;
     }
     xyFreqResp.Graphs[0].Color = RGB(0, 255, 0);
     //________________________________________________________ xyImpulseResponse
     xyImpulseResponse.CaptionX = L"n";
     xyImpulseResponse.CaptionY = L"h[n]";
     xyImpulseResponse.MinX= -1.0;
     xyImpulseResponse.MaxX= 6.28;
     xyImpulseResponse.MinY= -0.7;
     xyImpulseResponse.MaxY= 0.7;
     xyImpulseResponse.DivisionCountY = 7;
     xyImpulseResponse.ColorMode = WIN_COLOR_MODE_DARK;
     xyImpulseResponse.TextColor = RGB(180, 180, 190);
     xyImpulseResponse.Graphs.Add();
     xyImpulseResponse.Graphs[0].Type = Win::Graph::impulse;
     xyImpulseResponse.Graphs[0].Color = RGB(0, 255,0);
     //
     radioLowPass.Checked = true;
     UpdateGraphs();
}

void RemezTest::UpdateGraphs()
{
     Win::HourGlassCursor hgc(true);
     const int length = tbxLength.IntValue;
     vector<Math::Remez::Band> bands;
     Math::Remez::Band band;
     valarray<double> h;
     valarray<double> freq;
     valarray<double> errorData;
     double error = 0.0;
     if (radioLowPass.Checked)
     {
          //_________________________________________________________ 1. LowPass
          band.w1 = 0.0; // rads
          band.w2 = 0.628; // rads
          band.priority = 0.1; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 0.942; // rads
          band.w2 = M_PI; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          //
          error = remez.ComputeImpulseResponse(bands, length, h);
     }
     else if (radioHighPass.Checked)
     {
          //_________________________________________________________ 2. HighPass
          band.w1 = 0.0; // rads
          band.w2 = 0.628; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 0.942; // rads
          band.w2 = M_PI; // rads
          band.priority = 0.1; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          error = remez.ComputeImpulseResponse(bands, length, h);
     }
     else if (radioBandPass.Checked)
     {
          //_________________________________________________________ 3. BandPass
          band.w1 = 0.0; // rads
          band.w2 = 0.628; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 1.257; // rads
          band.w2 = 2.199; // rads
          band.priority = 0.1; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 2.670; // rads
          band.w2 = M_PI; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          //
          error = remez.ComputeImpulseResponse(bands, length, h);
     }
     else if (radioBandStop.Checked)
     {
          //_________________________________________________________ 4. BandStop
          band.w1 = 0.0; // rads
          band.w2 = 0.628; // rads
          band.priority = 0.1; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 1.257; // rads
          band.w2 = 2.199; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 2.670; // rads
          band.w2 = M_PI; // rads
          band.priority = 0.1; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          error = remez.ComputeImpulseResponse(bands, length, h);
     }
     else if (radioHilbert.Checked)
     {
          //_________________________________________________________ 5. Hilbert Transform
          band.w1 = 0.314; // rads
          band.w2 = 2.83; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          error = remez.ComputeHilbertImpulseResponse(bands, length, h);
     }
     else if (radioDifferentiator.Checked)
     {
          //_________________________________________________________ 6. Differentiator
          band.w1 = 0.0; // rads
          band.w2 = 2.6; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 1.0; // from 0 to 1
          bands.push_back(band);
          //
          band.w1 = 2.9; // rads
          band.w2 = M_PI; // rads
          band.priority = 1.0; // from 0 to 1
          band.gain = 0.0; // from 0 to 1
          bands.push_back(band);
          error = remez.ComputeDifferentiatorImpulseResponse(bands, length, h);
     }
     //___________________________________________________________ 7. xyImpulseResponse
     const size_t len = h.size();
     int i;
     xyImpulseResponse.Graphs[0].SetPointCount(len);
     for(i = 0; i < (int)len; i++)
     {
          xyImpulseResponse.Graphs[0][i].x = i;
          xyImpulseResponse.Graphs[0][i].y = h[i];
     }
     xyImpulseResponse.MaxX= (int)len;
     xyImpulseResponse.RefreshAll();
     //___________________________________________________________ 8. xyFreqResp
     valarray<complex<double> > H; // H(exp(jw))
     Math::FIRFilter filter;
     filter.Create(h);
     filter.GetFrequencyResponse(freqRad, H);
     if (radioDifferentiator.Checked)
     {
          xyFreqResp.MinY = 0.0;
          xyFreqResp.MaxY = 3.0;
          xyFreqResp.DivisionCountY = 3;
          for (i = 0; i < (int)numPoints; i++)
          {
               xyFreqResp.Graphs[0][i].y = abs(H[i]);
          }
     }
     else
     {
          xyFreqResp.MinY = -100.0; // dB
          xyFreqResp.MaxY = 10.0; // dB
          xyFreqResp.DivisionCountY = 11;
          for (i = 0; i < (int)numPoints; i++)
          {
               if (H[i] == 0.0)
               {
                    xyFreqResp.Graphs[0][i].y = -200.0; // dB
               }
               else
               {
                    xyFreqResp.Graphs[0][i].y = 20.0 * log10(abs(H[i])); // dB
               }
          }
     }
     xyFreqResp.RefreshAll();
     //
     wchar_t text[256];
     _snwprintf_s(text, 256, _TRUNCATE, L"Error = %g", error);
     this->Text = text;
}

void RemezTest::sldLength_Hscroll(Win::Event& e)
{
     tbxLength.IntValue = sldLength.Position;
     UpdateGraphs();
}

void RemezTest::radioLowPass_Click(Win::Event& e)
{
     UpdateGraphs();
}

void RemezTest::radioHighPass_Click(Win::Event& e)
{
     UpdateGraphs();
}

void RemezTest::radioBandPass_Click(Win::Event& e)
{
     UpdateGraphs();
}

void RemezTest::radioBandStop_Click(Win::Event& e)
{
     UpdateGraphs();
}

void RemezTest::radioHilbert_Click(Win::Event& e)
{
     UpdateGraphs();
}

void RemezTest::radioDifferentiator_Click(Win::Event& e)
{
     UpdateGraphs();
}


RemezLowpass

RemezHighpass

RemezBandpass

RemezBandstop

RemezHilbert

RemezDiff

Kaiser Window

The Kaiser Window can be used to design digital Filters. It has two parameters, the length of the filter and beta.
La ventana de Kaiser puede ser usada para diseñar Filtros digitales. Este tiene dos parámetros, la longitud del filtro y la beta.

Problem 2
Create a project to test the Kaiser Window method to design a digital filter; your project must be called KaiserTest.
Cree un proyecto para probar el método de la ventana de Kaiser para diseñar un filtro digital; su proyecto debe ser llamado KaiserTest.

Solution A
Create a Wintempla Dialog Application. Using Wintempla create the GUI shown below. After inserting the slider for the length of the filter, double click the control and mark the HSCROLL event. Insert another slider for beta.
Cree una aplicación de diálogo de Wintempla. Usando Wintempla cree la GUI mostrada debajo. Después de insertar el slider para longitud del filtro, haga clic doble en el control y marque el evento de HSCROLL. Inserte otro slider para beta.

KaiserTestGui

Solution B
Edit the KaiserTest.h file as shown.
Edite el archivo KaiserTest.h como se muestra.

KaiserTest.h
#pragma once //______________________________________ KaiserTest.h
#include "resource.h"

class KaiserTest: public Win::Dialog
{
public:
     KaiserTest()
     {
     }
     ~KaiserTest()
     {
     }
     Math::FIRFilter filter;
     const size_t numPoints = 2048;
     valarray<double> freqRad;
     void UpdateGraphs();
protected:
     . . .
};


Solution C
Edit the KaiserTest.cpp file as shown.
Edite el archivo KaiserTest.cpp como se muestra.

KaiserTest.Cpp
. . .
void KaiserTest::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldLength
     sldLength.SetRange(2, 200);
     sldLength.Position = 10;
     tbxLength.IntValue = 10;
     //________________________________________________________ sldBeta
     sldBeta.SetRange(0, 3000);
     sldBeta.Position = 100;
     tbxBeta.DoubleValue = 1.0;
     //________________________________________________________ xyFreqResp
     xyFreqResp.CaptionX = L"Frequency";
     xyFreqResp.CaptionY = L"Amplitude dB";
     xyFreqResp.MinX= 0.0; // radians
     xyFreqResp.MaxX = M_PI; // radians
     xyFreqResp.MinY = -100.0; // dB
     xyFreqResp.MaxY = 0.0; // dB
     xyFreqResp.DivisionCountY = 5;
     xyFreqResp.Graphs.Add(numPoints);
     freqRad.resize(numPoints);
     const double deltaFreq = M_PI / (numPoints - 1);
     for (size_t i = 0; i < numPoints; i++)
     {
          freqRad[i]= i * deltaFreq;
          xyFreqResp.Graphs[0][i].x = i* deltaFreq;
          xyFreqResp.Graphs[0][i].y = -200.0;
     }
     //________________________________________________________ xyImpulseResponse
     xyImpulseResponse.CaptionX = L"n";
     xyImpulseResponse.CaptionY = L"h[n]";
     xyImpulseResponse.MinX= -1.0;
     xyImpulseResponse.MaxX= 6.28;
     xyImpulseResponse.MinY= -0.2;
     xyImpulseResponse.MaxY= 0.8;
     xyImpulseResponse.DivisionCountY = 5;
     xyImpulseResponse.Graphs.Add();
     xyImpulseResponse.Graphs[0].Type = Win::Graph::impulse;
     radioKaiser.Checked = true;
     //
     UpdateGraphs();
}

void KaiserTest::sldLength_Hscroll(Win::Event& e)
{
     tbxLength.IntValue = sldLength.Position;
     UpdateGraphs();
}

void KaiserTest::sldBeta_Hscroll(Win::Event& e)
{
     tbxBeta.DoubleValue = sldBeta.Position/100.0;
     UpdateGraphs();
}

void KaiserTest::UpdateGraphs()
{
     Win::HourGlassCursor hgc(true);
     const int filterLength = tbxLength.IntValue;
     double beta = tbxBeta.DoubleValue;
     if (radioBartlett.Checked) beta = BARTLETT_WINDOW;
     else if (radioHanning.Checked) beta = HANNING_WINDOW;
     else if (radioHamming.Checked) beta = HAMMING_WINDOW;
     else if (radioBlackman.Checked) beta = BLACKMAN_WINDOW;
     //
     valarray<double> h;
     filter.CreateLowPass(beta, filterLength, 0.5 * M_PI);
     filter.GetImpulseResponse(h);
     //________________________________________________________ xyImpulseResponse
     const size_t len = h.size();
     size_t i;
     xyImpulseResponse.Graphs[0].SetPointCount(len);
     for(i = 0; i < len; i++)
     {
          xyImpulseResponse.Graphs[0][i].x = i;
          xyImpulseResponse.Graphs[0][i].y = h[i];
     }
     xyImpulseResponse.MaxX= len;
     xyImpulseResponse.RefreshAll();
     //________________________________________________________ xyFreqResp
     valarray<complex<double> > H; // H(exp(jw))
     filter.GetFrequencyResponse(freqRad, H);
     for(i = 0; i < numPoints; i++)
     {
          if (abs(H[i]) == 0.0)
          {
               xyFreqResp.Graphs[0][i].y = -200.0; // dB
          }
          else
          {
               xyFreqResp.Graphs[0][i].y = 20.0 * log10(abs(H[i])); // dB
          }
     }
     xyFreqResp.RefreshAll();
}

void KaiserTest::radioKaiser_Click(Win::Event& e)
{
     UpdateGraphs();
}

void KaiserTest::radioBartlett_Click(Win::Event& e)
{
     UpdateGraphs();
}

void KaiserTest::radioHanning_Click(Win::Event& e)
{
     UpdateGraphs();
}

void KaiserTest::radioHamming_Click(Win::Event& e)
{
     UpdateGraphs();
}

void KaiserTest::radioBlackman_Click(Win::Event& e)
{
     UpdateGraphs();
}


KaiserTestKaiser

KaiserTestBartlett

KaiserTestHanning

KaiserTestHamming

KaiserTestBlackman

Problem 3
Create a Wintempla dialog application called FilterPlay to filter (in real time) a music wave file. After creating the project open the stdafx.h file and remove the comments from the line #define WIN_DAC_ADC_SUPPORT.
Cree una aplicación de diálogo de Wintempla llamada FilterPlay para filtrar (en tiempo real) un archivo de música wave. Después de crear el proyecto abra el archivo stdafx.h y remueva los comentarios de la línea #define WIN_DAC_ADC_SUPPORT.

Solution A
Open Wintempla to edit the GUI. Using theShow All Controls in ToolboxShow All Controls in Toolbox from the toolbar insert a DAC control as shown below (on the events tabs, be sure all events are unselected). Insert two labels, two textboxes, four radio buttons, two sliders (with the Hscroll event), a button, a XyChart and drop down list.
Abra Wintempla y edite la GUI. Usando theShow All Controls in ToolboxShow All Controls in Toolbox desde la barra de herramientas inserta un control DAC como se muestra debajo (en la pestaña de eventos, asegúrate de que todos los eventos estén deseleccionado). Inserta dos etiquetas, dos cajas de texto, cuatro radio buttons, dos sliders (con el evento HScroll), un botón, una XyChart y una lista desplegable.

FilterPlayGui

Solution B
Edit the FilterPlay.h file and the FilterPlay.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the FilterPlay class is derived from Mm::IAudioOut). Remember that an interface is used to pass a set of functions to another function or another object.
Edite los archivos FilterPlay.h y FilterPlay.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase FilterPlay se deriva de Mm::IAudioOut). Recuerde que una interface es usada para pasar un conjunto de funciones a otra función u objeto.

FilterPlay.h
#pragma once //______________________________________ FilterPlay.h
#include "Resource.h"
class FilterPlay: public Win::Dialog, public Mm::IAudioOut
{
public:
     FilterPlay()
     {
     }
     ~FilterPlay()
     {
     }
     const size_t numPoints = 2048;
     valarray<double> freqRad;
     Math::FIRFilter fir;
     const double sampFreqHz = 44100.0;
     Mm::WaveFile waveFile;
     void UpdateFilter();
     //______________________________________________________________ Mm::IAudioOut
     void AudioOutStarted(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution);
     void AudioOutData(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr);
     void AudioOutStopped();
protected:
     . . .
};


FilterPlay.cpp
. . .
void FilterPlay::Window_Open(Win::Event& e)
{
     //________________________________________________________ sldFc
     sldFc.SetRange(1, 15000);
     sldFc.Position = 6900;
     tbxFc.IntValue = 6900;
     //________________________________________________________ sldBeta
     sldBeta.SetRange(1, 1000);
     sldBeta.Position = 434;
     tbxBeta.DoubleValue = 4.34;
     //________________________________________________________ sldLength
     sldLength.SetRange(10, 100);
     sldLength.Position = 52;
     tbxLength.IntValue = 52;
     //________________________________________________________ xyFreqResp
     xyFreqResp.CaptionX = L"Frequency (Hz)";
     xyFreqResp.CaptionY = L"|H[f]| dB";
     xyFreqResp.MinX = 0.0;
     xyFreqResp.MaxX = sampFreqHz / 2.0;
     xyFreqResp.MinY = -90.0; // dB
     xyFreqResp.MaxY = 10.0; // dB
     xyFreqResp.DivisionCountX = 11;
     xyFreqResp.DivisionCountY = 5;
     xyFreqResp.ColorMode = WIN_COLOR_MODE_DARK;
     xyFreqResp.Graphs.Add((int)numPoints);
     freqRad.resize(numPoints);
     const double deltaFreq = M_PI / (numPoints - 1);
     for (int i = 0; i < (int)numPoints; i++)
     {
          freqRad[i] = i * deltaFreq;
          xyFreqResp.Graphs[0][i].x = (i * deltaFreq * sampFreqHz) / (2.0 * M_PI);
          xyFreqResp.Graphs[0][i].y = -200.0;
     }
     xyFreqResp.Graphs[0].Color = RGB(0, 255, 0);
     xyFreqResp.TextColor = RGB(255, 255, 255);
     xyFreqResp.RefreshAll();
     //________________________________________________________ ddDevice
     const int count = ::waveOutGetNumDevs();
     WAVEOUTCAPS woc;
     const int wsize = sizeof(WAVEOUTCAPS);
     for (int i = 0; i < count; i++)
     {
          if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR)
          {
               ddDevice.Items.Add(woc.szPname, i);
          }
     }
     ddDevice.SelectedIndex = 0;
     //________________________________________________________ xyImpulseResponse
     xyImpulseResponse.CaptionX = L"n";
     xyImpulseResponse.CaptionY = L"h[n]";
     xyImpulseResponse.MinX = -1.0;
     xyImpulseResponse.MaxX = 6.28;
     xyImpulseResponse.MinY = -0.8;
     xyImpulseResponse.MaxY = 0.8;
     xyImpulseResponse.DivisionCountY = 4;
     xyImpulseResponse.Graphs.Add();
     xyImpulseResponse.Graphs[0].Type = Win::Graph::impulse;
     xyImpulseResponse.Graphs[0].Color = RGB(0, 255, 0);
     xyImpulseResponse.SetColorMode(WIN_COLOR_MODE_DARK);
     xyImpulseResponse.TextColor = RGB(255, 255, 255);
     //
     this->btPlay.Enabled = true;
     this->btStop.Enabled = false;
     radioLowpass.Checked = true;
     UpdateFilter();
}

void FilterPlay::sldFc_Hscroll(Win::Event& e)
{
     tbxFc.IntValue = sldFc.Position;
     UpdateFilter();
}

void FilterPlay::sldBeta_Hscroll(Win::Event& e)
{
     tbxBeta.DoubleValue = sldBeta.Position / 100.0;
     UpdateFilter();
}

void FilterPlay::sldLength_Hscroll(Win::Event& e)
{
     tbxLength.IntValue = sldLength.Position;
     UpdateFilter();
}

void FilterPlay::UpdateFilter()
{
     const int length = tbxLength.IntValue;
     const double nyquist = sampFreqHz / 2.0;
     const double wc = M_PI * tbxFc.IntValue / nyquist;
     const double beta = tbxBeta.DoubleValue;
     //
     if (radioLowpass.Checked)
     {
          filter.CreateLowPass(beta, length, wc);
     }
     else if (radioBandpass.Checked)
     {
          filter.CreateBandPass(beta, length, wc, wc + 0.5);
     }
     else if (radioBandstop.Checked)
     {
          filter.CreateBandStop(beta, length, wc, wc + 0.5);
     }
     else if (radioHighpass.Checked)
     {
          filter.CreateHighPass(beta, length, wc);
     }
     else if (radioHilbert.Checked)
     {
          filter.CreateHilbertTransform(beta, length);
     }
     else if (radioDifferentiator.Checked)
     {
          filter.CreateDifferentiator(beta, length);
     }
     if (filter.IsSymmetric()) xyImpulseResponse.Graphs[0].Caption = L"SYMMETRIC";
     else if (filter.IsAntisymmetric()) xyImpulseResponse.Graphs[0].Caption = L"ANTISYMMETRIC";
     else xyImpulseResponse.Graphs[0].Caption = L"Impulse Response";
     valarray<double> h;
     filter.GetImpulseResponse(h);
     //________________________________________________________ xyImpulseResponse
     const int len = (int)h.size();
     int i;
     xyImpulseResponse.Graphs[0].SetPointCount(len);
     for (i = 0; i < len; i++)
     {
          xyImpulseResponse.Graphs[0][i].x = i;
          xyImpulseResponse.Graphs[0][i].y = h[i];
     }
     xyImpulseResponse.MaxX = len;
     xyImpulseResponse.RefreshAll();
     //________________________________________________________ xyFreqResp
     valarray<complex<double> > H; // H(exp(jw))
     filter.GetFrequencyResponse(freqRad, H);
     for (int i = 0; i < numPoints; i++)
     {
          if (abs(H[i]) == 0.0)
          {
               xyFreqResp.Graphs[0][i].y = -200.0; // dB
          }
          else
          {
               xyFreqResp.Graphs[0][i].y = 20.0 * log10(abs(H[i])); // dB
          }
     }
     xyFreqResp.RefreshAll();
}

void FilterPlay::btStop_Click(Win::Event& e)
{
     dacOutput.Stop();
}

void FilterPlay::btPlay_Click(Win::Event& e)
{
     //________________________________________________________ 1. Prompt to the user to get the filename
     Win::FileDlg dlg;
     dlg.Clear();
     dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav");
     if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return;
     //________________________________________________________ 2. Get the Device ID
     LPARAM deviceID = WAVE_MAPPER;
     ddDevice.GetSelectedData(deviceID);
     //________________________________________________________ 3. Open the Wave File
     const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath());
     if (error != NULL)
     {
          this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR);
          return;
     }
     //________________________________________________________ 4. Start the DAC
     const unsigned int numChannels = waveFile.GetNumChannels();
     const unsigned int bitsResolution = waveFile.GetBitsResolution();
     const unsigned int numBytes = 4096 * numChannels * bitsResolution / 8;
     error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSampFreqHz(), numChannels, bitsResolution, numBytes, this);
     if (error != NULL)
     {
          this->MessageBox(error, L"FilterPlay", MB_OK | MB_ICONERROR);
          return;
     }
}

void FilterPlay::AudioOutStarted(unsigned int sampFreqHz, unsigned int numbChannels, unsigned int bitsResolution)
{
     btPlay.Enabled = false;
     btStop.Enabled = true;
     EnableCloseButton(false);
}

void FilterPlay::AudioOutData(unsigned int sampFreqHz, unsigned int numbChannels, unsigned int bitsResolution, WAVEHDR* waveHdr)
{
     waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength);
     Sys::Sample16* samples = (Sys::Sample16*)waveHdr->lpData;
     const size_t numSamples = waveHdr->dwBytesRecorded / 4;
     filter.ComputeOutput(samples, numSamples);
}

void FilterPlay::AudioOutStopped()
{
     btPlay.Enabled = true;
     btStop.Enabled = false;
     waveFile.Close();
     EnableCloseButton(true);
}

void FilterPlay::radioLowpass_Click(Win::Event& e)
{
     UpdateFilter();
}

void FilterPlay::radioBandpass_Click(Win::Event& e)
{
     UpdateFilter();
}

void FilterPlay::radioBandstop_Click(Win::Event& e)
{
     UpdateFilter();
}

void FilterPlay::radioHighpass_Click(Win::Event& e)
{
     UpdateFilter();
}

void FilterPlay::radioHilbert_Click(Win::Event& e)
{
     UpdateFilter();
}

void FilterPlay::radioDifferentiator_Click(Win::Event& e)
{
     UpdateFilter();
}


Solution C
Run the program and click the Play button. Select a wave file with two channels, 44100 Hz and 16 bits.
Ejecute el programa y haga clic en el botón de Play. Seleccione un archivo Wave con dos canales, 44100 Hz y 16 bits.

FilterPlayLowPass

FilterPlayHighPass

FilterPlayBandPass

FilterPlayBandStop

FilterPlayHilbertEven

FilterPlayHilbertOdd

FilterPlayDiff

Frequency Sampling

It is a method to design digital FIR filters with a custom frequency response.
Este es un método para diseñar FIR filtros digitales con una respuesta en frecuencia particular.

Problem 4
Create a Wintempla Dialog Application called Loudfir to implement a lowpass filter. Using Wintempla create the GUI shown below.
Cree una aplicación de diálogo de Wintempla llamada Loudfir para implementar un filtro pasa bajas. Usando Wintempla cree la GUI mostrada debajo.

LoudfirGui

Solution A
Edit the Loudfir.h file as shown.
Edite el archivo Loudfir.h como se muestra.

Loudfir.h
#pragma once //______________________________________ Loudfir.h
#include "Resource.h"
class Loudfir: public Win::Dialog
{
public:
     Loudfir()
     {
     }
     ~Loudfir()
     {
     }
     Math::FIRFilter filter;
     const double sampFreqHz = 44100.0;
     void Desired();
     void ImpulseResponse();
     void FrequencyResponse();
protected:
     . . .
};


Solution B
Edit the Loudfir.cpp file as shown.
Edite el archivo Loudfir.cpp como se muestra.

Loudfir.Cpp
. . .
void Loudfir::Window_Open(Win::Event& e)
{
     Desired();
     ImpulseResponse();
     FrequencyResponse();
}

void Loudfir::Desired()
{
     //_______________________________________________ Example 1. DSP Proakis Example 8.2.1, page 632
     constexpr double delta = (2.0 * M_PI) / 15.0;
     vector<Math::FIRFilter::Sample> sample = { {0, 0}, {delta, 0}, {2 * delta, 0}, {3 * delta, 0},
          {4 * delta, 20.0 * log10(0.4)}, {5 * delta, -200.0}, {6 * delta, -200.0}, {7 * delta, -200.0} };
     //_______________________________________________ Example 2. DSP Proakis Example 8.2.2, page 634
     //constexpr double delta = (2.0 * M_PI) / 32.0;
     //vector<Math::FIRFilter::Sample> sample = { {0, 0}, {delta, 0}, {2 * delta, 0}, {3 * delta, 0},
     //     {4 * delta, 0}, {5 * delta, 0}, {6 * delta, 20.0 * log10(0.3789795)}, {7 * delta, -200.0}, {8 * delta, -200.0},
     //     {9 * delta, -200.0}, {10 * delta, -200.0}, {11 * delta, -200.0}, {12 * delta, -200.0},
     //     {13 * delta, -200.0}, {14 * delta, -200.0}, {15 * delta, -200.0} };
     int i;

     xyDesired.CaptionX = L"Frequency (Hz)";
     xyDesired.CaptionY = L"Amplitude (dB)";
     xyDesired.MinX = 10.0; // Hz
     xyDesired.MaxX = 100000.0; // Hz
     xyDesired.MinY = -60.0; // dB
     xyDesired.MaxY = 0.0; // dB
     xyDesired.SetLogScaleX(true);
     xyDesired.DivisionCountX = 4;
     xyDesired.DivisionCountY = 6;
     xyDesired.Graphs.Add((int)sample.size());
     for (i = 0; i < (int)sample.size(); i++)
     {
          xyDesired.Graphs[0][i].x = (sample[i].freqRad * sampFreqHz) / (2.0 * M_PI); // From rads to Hz
          xyDesired.Graphs[0][i].y = sample[i].gain_dB;
     }
     xyDesired.ColorMode = WIN_COLOR_MODE_DARK;
     xyDesired.Graphs[0].Color = RGB(0, 200, 0);
     xyDesired.Graphs[0].Caption = L"Desired frequency response";
     xyDesired.TextColor = RGB(255, 255, 255);
     xyDesired.RefreshAll();
     filter.CreateFromFreqSamples(sample);
}

void Loudfir::ImpulseResponse()
{
     valarray<double> h;
     filter.GetImpulseResponse(h);
     const int len = (int)h.size();
     xyImpResp.CaptionX = L"Axis X";
     xyImpResp.CaptionY = L"h[n]";
     xyImpResp.MinX = 0.0;
     xyImpResp.MaxX = len;
     xyImpResp.MinY = -1.0;
     xyImpResp.MaxY = 1.0;
     xyImpResp.Graphs.Add(len);
     for (int i = 0; i < len; i++)
     {
          xyImpResp.Graphs[0][i].x = i;
          xyImpResp.Graphs[0][i].y = h[i];
     }
     xyImpResp.Graphs[0].Color = RGB(200, 200, 0);
     xyImpResp.Graphs[0].Type = Win::Graph::impulse;
     xyImpResp.Graphs[0].Caption = L"Impulse Response";
     xyImpResp.ColorMode = WIN_COLOR_MODE_DARK;
     xyImpResp.TextColor = RGB(255, 255, 255);
     xyImpResp.RefreshAll();
}

void Loudfir::FrequencyResponse()
{
     const int numFreq = 1024;
     const double deltaFreqRad = M_PI / (numFreq - 1.0);
     int i = 0;
     valarray<double> freqRad;
     freqRad.resize(numFreq);
     for (i = 0; i < numFreq; i++) freqRad[i] = i * deltaFreqRad;
     valarray<complex<double> > H; // H(exp(jw))
     filter.GetFrequencyResponse(freqRad, H);
     xyFreqResp.CaptionX = L"Frequency (Hz)";
     xyFreqResp.CaptionY = L"Amplitude (dB)";
     xyFreqResp.MinX = 10.0; // Hz
     xyFreqResp.MaxX = 100000.0; // Hz
     xyFreqResp.MinY = -60.0; // dB
     xyFreqResp.MaxY = 0.0; // dB
     xyFreqResp.SetLogScaleX(true);
     xyFreqResp.DivisionCountX = 4;
     xyFreqResp.DivisionCountY = 6;
     xyFreqResp.Graphs.Add(numFreq);
     for (i = 0; i < numFreq; i++)
     {
          xyFreqResp.Graphs[0][i].x = (sampFreqHz * freqRad[i]) / (2.0 * M_PI);
          if (abs(H[i]) == 0.0)
          {
               xyFreqResp.Graphs[0][i].y = -200.0; // dB
          }
          else
          {
               xyFreqResp.Graphs[0][i].y = 20.0 * log10(abs(H[i])); // dB
          }
     }
     xyFreqResp.ColorMode = WIN_COLOR_MODE_DARK;
     xyFreqResp.Graphs[0].Color = RGB(0, 200, 0);
     xyFreqResp.Graphs[0].Caption = L"Frequency response";
     xyFreqResp.TextColor = RGB(255, 255, 255);
     xyFreqResp.RefreshAll();
}


LoudfirRun1

LoudfirRun2

© Copyright 2000-2026 Wintempla selo. All Rights Reserved. abr. 17 2026. Home